import { _decorator, AudioClip, AudioSource, Color, Component, EventTouch, find, instantiate, Label, Node, PolygonCollider2D, Prefab, resources, Sprite, SpriteFrame, UITransform, Vec2, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
import { ColorBoard } from './ColorBoard';
import { Toolbox } from './Toolbox';
import { Utils } from './Utils';
import { Player } from './Player';
import { Backpack } from './Backpack';
import { GameManager } from './GameManager';
import { EquipmentBpBox } from './EquipmentBpBox';

@ccclass('Blueprint')
export class Blueprint extends Component {
    @property({type: Node})
    equipmentInfoNode: Node = null!

    @property({type: Node})
    blueprintCoverNode: Node = null!

    @property({type: Node})
    indicatorCoverNode: Node = null!

    @property({type: Node})
    itemIndicatorNode: Node = null!

    @property({type: Node})
    colorBoardNode: Node = null!

    @property({type: Node})
    toolBoxNode: Node = null!

    @property({type: Node})
    equipmentBpBoxNode: Node = null!

    @property({type: Node})
    aiResultBoardNode: Node = null!

    @property({type: Prefab})
    colorItemPrefab: Prefab = null!

    @property({type: Prefab})
    magnifierPrefab: Prefab = null!

    @property({type: AudioClip})
    addAudio: AudioClip = null!

    @property({type: AudioClip})
    delAudio: AudioClip = null!

    @property({type: AudioClip})
    clickAudio: AudioClip = null!

    @property({type: AudioClip})
    wrongAudio: AudioClip = null!

    undoStack = []
    redoStack = []
    maxStackLength = 150

    isAIChecking: boolean = false
    isEquipOrStore: string = ''
    addOrDel: string = 'Add'
    equipmentInfo: object = null
    selectedPixelColor: Color | null = null
    frameSelectedItemArray: Array<Node> = []

    utils: Utils = null!
    toolBoxComp: Toolbox = null!
    audioSource: AudioSource = null!
    colorBoardComp: ColorBoard = null!
    equipmentBpBoxComp: EquipmentBpBox = null!

    start() {
        this.initComponents()
        this.initTouchEvents()
        this.setCover()
    }

    /** 
     * @zh
     * 初始化组件。
    */
    initComponents() {
        this.audioSource = find('Canvas').getComponent(AudioSource)
        this.colorBoardComp = this.colorBoardNode.getComponent(ColorBoard)
        this.toolBoxComp = this.toolBoxNode.getComponent(Toolbox)
        this.equipmentBpBoxComp = this.equipmentBpBoxNode.getComponent(EquipmentBpBox)
        this.utils = find('Canvas').getComponent(Utils)
    }

   /** 
     * @zh
     * 初始化触摸事件。
    */
    initTouchEvents() {
        this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this)
        this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this)
        this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this)
        this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchCancel, this)
    }

    /** 
     * @zh
     * 初始开始时执行的逻辑。
    */
    onTouchStart(event: EventTouch) {
        if (this.toolBoxComp.selectedTool == 'frameSelect') {
            this.addToUndoStack()
            this.clearRedoStack()
            this.frameSelectedItemArray = []
            this.itemIndicatorNode.active = false
        }
    }

    /** 
     * @zh
     * 触摸移动时执行的逻辑。
    */
    onTouchMove(event: EventTouch) {
        let loc = event.getUILocation()
        let pos = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(loc.x, loc.y, 0))

        // 如果当前选择的是框选工具
        if (this.toolBoxComp.selectedTool == 'frameSelect') {
            for (let i=0; i<this.node.children.length; i++) {
                let item = this.node.children[i]
                if (item.getComponent(UITransform).getBoundingBox().contains(new Vec2(pos.x, pos.y))) {
                    this.frameSelectedItemArray.push(item)
                    break
                }
            }
            
            // 更新框选区域
            this.updateFrameSelectItemIndicator()
        }
        else {
            // 框选区域存在的话，不显示单个像素块指示框
            if (this.frameSelectedItemArray.length) {
                return
            }

            // 玩家触摸到哪一个像素块，该像素块就显示指示框
            for (let i=0; i<this.node.children.length; i++) {
                let item = this.node.children[i]
                let itemUITransform = item.getComponent(UITransform)
                if (item.getComponent(UITransform).getBoundingBox().contains(new Vec2(pos.x, pos.y))) {
                    this.itemIndicatorNode.active = true
                    this.itemIndicatorNode.getComponent(UITransform).width = itemUITransform.width * 1.2
                    this.itemIndicatorNode.getComponent(UITransform).height = itemUITransform.height * 1.2
                    this.itemIndicatorNode.setPosition(item.position)
                    break
                }
            }
        }
    }

    /** 
     * @zh
     * 触摸结束时执行的逻辑。
    */
    onTouchEnd(event: EventTouch) {
        let loc = event.getUILocation()
        let pos = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(loc.x, loc.y, 0))
        
        for (let i=0; i<this.node.children.length; i++) {
            let item = this.node.children[i]
            if (!item.getComponent(UITransform).getBoundingBox().contains(new Vec2(pos.x, pos.y))) {
                continue
            }
            
            // 如果是画笔工具
            if (this.toolBoxComp.selectedTool == 'pencil') {

                // 没有选中制作图的话提示玩家
                if (!this.checkIsBlueprintSelected()) {
                    return
                }
       
                // 没有选中颜料的话提示玩家
                if (!this.checkIsColorSelected()) {
                    return
                }
                
                // 添加指定颜色像素块
                this.addColorItem(item.name, item.position, this.colorBoardComp.selectedColor)
            }

            // 如果是橡皮擦工具
            else if (this.toolBoxComp.selectedTool == 'eraser') {
                this.delColorItem(item.position)
            }

            // 如果是吸管工具
            else if (this.toolBoxComp.selectedTool == 'colorPicker') {
                this.pickColor(item.position)
            }

            // 如果是框选工具
            else if (this.toolBoxComp.selectedTool == 'frameSelect') {
                // onTouchMove中已实现功能
            }

            // 如果时油漆桶工具
            else if (this.toolBoxComp.selectedTool == 'paintBucket') {
                // 没有选中制作图的话提示玩家
                if (!this.checkIsBlueprintSelected()) {
                    return
                }
        
                // 没有选中颜色的话提示玩家
                if (!this.checkIsColorSelected()) {
                    return
                }

                // 填充
                this.addToUndoStack()
                this.clearRedoStack()
                let newColorHex = this.colorBoardComp.selectedColor
                let oldColorHex = item.getComponent(Sprite).color.toHEX()
                this.floodFill4(item.position.x, item.position.y, newColorHex, oldColorHex)
            }
            break
        }
    }

    /** 
     * @zh
     * 触摸取消时执行的逻辑。
    */
    onTouchCancel() {
        // 隐藏指示框
        this.itemIndicatorNode.active = false
    }

    /** 
     * @zh
     * 检查玩家是否已经选择了一张制作图。
    */
    checkIsBlueprintSelected() {
        if (!this.equipmentBpBoxComp.selectedEquipmentBpBoxItem) {
            this.utils.showFlashMessage('需要从左侧选择一张制作图', 1)
            this.audioSource.playOneShot(this.wrongAudio)
            return false
        }
        return true
    }

    /** 
     * @zh
     * 检查玩家是否已经选择了颜料。
    */
    checkIsColorSelected() {
        if (!this.colorBoardComp.selectedColor) {
            if (Object.keys(this.colorBoardComp.colorData).length) {
                this.utils.showFlashMessage('需要从左侧选择一种颜料', 1)
                this.audioSource.playOneShot(this.wrongAudio)
            }
            else {
                // 颜料全部用完时候的提示
                this.utils.showFlashMessage('颜料不足，可以用橡皮擦回收颜料', 1)
                this.audioSource.playOneShot(this.wrongAudio) 
            }
            return false
        }
        return true
    }

    /** 
     * @zh
     * 检查坐标是否在框选范围中。
    */
    checkIsInSelectedFrame(pos: Vec3) {
        if (!this.frameSelectedItemArray.length) {
            return true
        }

        if (this.itemIndicatorNode.getComponent(UITransform).getBoundingBox().contains(new Vec2(pos.x, pos.y))) {
            return true
        }

        return false
    }

    /** 
     * @zh
     * 更新框选指示框大小。
    */
    updateFrameSelectItemIndicator() {
        // 根据左上角和右下角这两个item的位置设置指示框位置和大小
        let firstSelectedItem = this.frameSelectedItemArray[0]
        let lastSelectedItem = this.frameSelectedItemArray[this.frameSelectedItemArray.length-1]

        let indicatorX = (firstSelectedItem.position.x + lastSelectedItem.position.x)/2
        let indicatorY = (firstSelectedItem.position.y + lastSelectedItem.position.y)/2
        let indicatorWidth = Math.abs(firstSelectedItem.position.x - lastSelectedItem.position.x) + firstSelectedItem.getComponent(UITransform).width
        let indicatorHeight = Math.abs(firstSelectedItem.position.y - lastSelectedItem.position.y) + firstSelectedItem.getComponent(UITransform).width
        this.itemIndicatorNode.setPosition(indicatorX, indicatorY, 0)
        this.itemIndicatorNode.getComponent(UITransform).setContentSize(indicatorWidth, indicatorHeight)

        // 防止数组再onTouchMove中变得过大，另外其实也只需要保留第一个和最后一个元素的值
        let temp = []
        if (this.frameSelectedItemArray.length >= 3) {
            temp.push(this.frameSelectedItemArray[0])
            temp.push(this.frameSelectedItemArray[this.frameSelectedItemArray.length-1])
            this.frameSelectedItemArray = temp
        }

        // 显示方框
        this.itemIndicatorNode.active = true
    }

    /** 
     * @zh
     * 油漆桶泛洪填充（四邻域）。
    */
    floodFill4(x: number, y: number, newColorHex: string, oldColorHex: string) {
        // 本游戏中填色不是将blueprint上的item直接改色
        // 而是在blueprintCover节点上生成一个目标颜色item，所以如果blueprintCover某个坐标上已经有item了，需要直接返回，不再填色。
        let blueprintItem = this.getBlueprintItemByPos(x, y)
        let blueprintCoverItem = this.getBlueprintCoverItemByPos(x, y)
        if (!blueprintItem || blueprintCoverItem || !this.checkIsInSelectedFrame(new Vec3(x, y, 0))) {
            return
        }

        let nodeWidth = this.node.getComponent(UITransform).width
        let itemWidth = blueprintItem.getComponent(UITransform).width
        let currentItemColorHex = blueprintItem.getComponent(Sprite).color.toHEX()
        if (this.colorBoardComp.selectedColor && blueprintItem && x>-nodeWidth/2 && x<nodeWidth/2 && y>-nodeWidth/2 && y<nodeWidth/2 && currentItemColorHex==oldColorHex) {
            this.addColorItem(blueprintItem.name, blueprintItem.position, newColorHex)
            this.floodFill4(x+itemWidth, y, newColorHex, oldColorHex)
            this.floodFill4(x-itemWidth, y, newColorHex, oldColorHex)
            this.floodFill4(x, y+itemWidth, newColorHex, oldColorHex)
            this.floodFill4(x, y-itemWidth, newColorHex, oldColorHex)
        }
    }

    /** 
     * @zh
     * 根据坐标获取Blueprint上的某个item节点。
    */
    getBlueprintItemByPos(x: number, y: number) {
        for (let i=0; i<this.node.children.length; i++) {
            let item = this.node.children[i]
            if (item.getComponent(UITransform).getBoundingBox().contains(new Vec2(x, y))) {
                return item
            }
        }
        return null
    }

    /** 
     * @zh
     * 根据坐标获取BlueprintCover上的某个item节点。
    */
    getBlueprintCoverItemByPos(x: number, y: number) {
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let colorItem = this.blueprintCoverNode.children[i]
            if (colorItem.getComponent(UITransform).getBoundingBox().contains(new Vec2(x, y))) {
                return colorItem
            }
        }
        return null
    }

    /** 
     * @zh
     * Blueprint Cover节点以及Indicator Cover的大小与位置跟Blueprint节点的永远一样。
    */
    setCover() {
        this.blueprintCoverNode.getComponent(UITransform).width = this.node.getComponent(UITransform).width
        this.blueprintCoverNode.getComponent(UITransform).height = this.node.getComponent(UITransform).height
        this.blueprintCoverNode.setPosition(this.node.position)

        this.indicatorCoverNode.getComponent(UITransform).width = this.node.getComponent(UITransform).width
        this.indicatorCoverNode.getComponent(UITransform).height = this.node.getComponent(UITransform).height
        this.indicatorCoverNode.setPosition(this.node.position)
    }

    /** 
     * @zh
     * 生成颜色像素块预制。
    */
    spawnColorItem(name: string, pos: Vec3, colorHex: string) {
        // 名字中带有序号，用于后期对应位置颜色验证
        let colorItem = instantiate(this.colorItemPrefab)
        colorItem.name = name
        colorItem.setPosition(pos)
        colorItem.getComponent(Sprite).color = new Color().fromHEX(colorHex)
        colorItem.getComponent(UITransform).width = this.node.children[0].getComponent(UITransform).width
        colorItem.getComponent(UITransform).height = this.node.children[0].getComponent(UITransform).height
        return colorItem
    }

    /** 
     * @zh
     * 在指定位置添加指定颜色像素块。
    */
    addColorItem(name: string, pos: Vec3, colorHex: string) {
        // 如果当前位置已经添加了像素块，则直接返回
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let colorItem = this.blueprintCoverNode.children[i]
            if (colorItem.getComponent(UITransform).getBoundingBox().contains(new Vec2(pos.x, pos.y))) {
                return
            }
        }

        // 如果有框选的区域，那只能对框选中的像素块起作用
        if (!this.checkIsInSelectedFrame(pos)) {
            this.utils.showFlashMessage('无法在框选区域外执行操作', 0.8)
            this.audioSource.playOneShot(this.wrongAudio)
            return
        }

        this.addToUndoStack()
        this.clearRedoStack()

        // 添加像素块
        let colorItem = this.spawnColorItem(name, pos, colorHex)
        this.blueprintCoverNode.addChild(colorItem)

        // 播放音频
        this.audioSource.playOneShot(this.addAudio)

        // 颜料数量-1
        this.colorBoardComp.updateColorData(colorHex, -1)
    }

    /** 
     * @zh
     * 删除指定位置的像素块。
    */
    delColorItem(pos: Vec3) {
        // 如果有框选的区域，那只能对框选中的item起作用
        if (!this.checkIsInSelectedFrame(pos)) {
            this.utils.showFlashMessage('无法在框选区域外执行操作', 0.8)
            this.audioSource.playOneShot(this.wrongAudio)
            return
        }

        // 删除对应位置上的colorItem
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let colorItem = this.blueprintCoverNode.children[i]
            if (colorItem.getComponent(UITransform).getBoundingBox().contains(new Vec2(pos.x, pos.y))) {
                this.addToUndoStack()
                this.clearRedoStack()
                
                colorItem.removeFromParent()
                
                // 颜料数量回收+1
                let colorHex = colorItem.getComponent(Sprite).color.toHEX()
                this.colorBoardComp.updateColorData(colorHex, 1)

                // 播放音频
                this.audioSource.playOneShot(this.delAudio)
                
                return
            } 
        }

        this.utils.showFlashMessage('这里没有可以擦除的像素块', 0.8)
        this.audioSource.playOneShot(this.delAudio)
    }

    /** 
     * @zh
     * 吸管吸取颜色。
    */
    pickColor(pos: Vec3) {
        for (let i=0; i<this.node.children.length; i++) {
            let item = this.node.children[i]
            if (item.getComponent(UITransform).getBoundingBox().contains(new Vec2(pos.x, pos.y))) {
                let color = item.getComponent(Sprite).color.toHEX()

                // 如果是白色，则表示没有可吸取的颜色（目前制作图上不会出现白色颜料），后面需要白色的话可以通过节点名称来判断
                if (color == 'ffffff') {
                    this.utils.showFlashMessage('这没有可吸取的颜色', 2)
                    this.audioSource.playOneShot(this.wrongAudio)
                    return
                }

                // 如果不等于白色，则从颜料板上看看有没有匹配的颜料
                else {
                    let colorContentNode = this.colorBoardComp.colorContentNode
                    for (let j=0; j<colorContentNode.children.length; j++) {
                        let colorItem = colorContentNode.children[j]
                        if (colorItem.getComponent(Sprite).color.toHEX() == color) {
                            this.colorBoardComp.selectOneColorItem(colorItem)
                            this.utils.showFlashMessage('已在左侧选择对应颜料')
                            return
                        }
                    }

                    this.utils.showFlashMessage('没有这种颜料')
                    this.audioSource.playOneShot(this.wrongAudio)
                    return
                }
            }
        }
    }

    /** 
     * @zh
     * 设置制作图上的装备信息。
    */
    setEquipmentInfo(equipmentInfo: object) {
        if (!equipmentInfo) {
            // 装备说明文本设置为空
            this.equipmentInfo = null
            this.equipmentInfoNode.getComponent(Label).string = '未选择制作图'
        }
        else {
            this.equipmentInfo = JSON.parse(JSON.stringify(equipmentInfo))  // 防止后面对原始equipmentJsonData变量进行修改
            this.equipmentInfoNode.getComponent(Label).string = equipmentInfo['intro']
        }
    }

    /** 
     * @zh
     * 玩家点击了使用按钮。
    */
    onBlueprintEquipButtonClicked() {
        // 检查是否已经绘制完毕
        if (!this.checkIsDrawingFinished()) {
            return
        }

        // 播放点击音效
        this.audioSource.playOneShot(this.clickAudio)

        // 如果是武器
        if (this.equipmentInfo['type'] == 'weapon') {
            this.utils.showFlashMessage(`已装备${this.equipmentInfo['zh']}!`, 1)
            
            localStorage.setItem('lp-weapon', this.equipmentInfo['en'])

            // 清空自定义武器颜色
            let customWeaponNode = find('Canvas/Player/Weapon/Custom Weapon')
            for (let i =0; i<customWeaponNode.children.length; i++) {
                customWeaponNode.children[i].getComponent(Sprite).color = new Color(0, 0, 0, 0)
            }

            // 给主角装备武器
            let self = this
            resources.load(this.equipmentInfo['picPath'], SpriteFrame, (err: any, spriteFrame: SpriteFrame) => {
                find('Canvas/Player/Weapon').getComponent(Sprite).spriteFrame = spriteFrame
                find('Canvas/Player').getComponent(Player).weaponInfo = self.equipmentInfo

                // 更新拥有的制作图数量
                self.equipmentBpBoxComp.updateEquipmentBpBoxInfo('DEL', self.equipmentInfo['en'], 1)
                self.equipmentInfo = null
            })
        }

        // 如果是自定义武器
        else if (this.equipmentInfo['type'] == 'customWeapon') {
            if (this.isAIChecking) {
                this.utils.showFlashMessage('AI正在鉴定中...', 1)
                return
            }

            // AI检测画的质量，AI给的分数越高，攻击力越高
            this.AICheck()
            this.isEquipOrStore = 'equip'
            this.isAIChecking = true

            // 从服务器获取AI评测数据是异步的，所以像画板清空这些操作需要在AI返回之后再执行
            return
        }

        // 如果是帽子防具
        else if (this.equipmentInfo['type'] == 'hat') {
            this.utils.showFlashMessage(`已装备${this.equipmentInfo['zh']}!`, 1)
            localStorage.setItem('lp-hat', this.equipmentInfo['en'])

            // 给主角装备帽子
            let self = this
            resources.load(this.equipmentInfo['picPath'], SpriteFrame, (err: any, spriteFrame: SpriteFrame) => {
                find('Canvas/Player/Hat').getComponent(Sprite).spriteFrame = spriteFrame
                find('Canvas/Player').getComponent(Player).defense = 1
                find('Canvas/Player').getComponent(Player).defense += self.equipmentInfo['defense']

                // 更新拥有的制作图数量
                self.equipmentBpBoxComp.updateEquipmentBpBoxInfo('DEL', self.equipmentInfo['en'], 1)
                self.equipmentInfo = null
            })
        }

        // 如果是治疗药水
        else if (this.equipmentInfo['type'] == 'cure') {
            find('Canvas/Player').getComponent(Player).recoverLife(this.equipmentInfo['value'])
            this.utils.showFlashMessage(`已恢复${this.equipmentInfo['value']}点血量!`, 1)

            // 更新拥有的制作图数量
            this.equipmentBpBoxComp.updateEquipmentBpBoxInfo('DEL', this.equipmentInfo['en'], 1)
            this.equipmentInfo = null
        }
        
        // 如果是技能
        else if (this.equipmentInfo['type'] == 'skill') {
            // this.utils.showFlashMessage(`${this.equipmentInfo['zh']}!!!`, 2)
            if (this.equipmentInfo['en'] == 'lightBeam') {
                find('Canvas/Player').getComponent(Player).showLightBeamAttack()
            }
            else if (this.equipmentInfo['en'] == 'fireball') {
                find('Canvas/Player').getComponent(Player).showFireballAttack()
            }
            else if (this.equipmentInfo['en'] == 'lightning') {
                find('Canvas/Player').getComponent(Player).showLightningAttack()
            }
            else if (this.equipmentInfo['en'] == 'trashBin') {
                find('Canvas/Player').getComponent(Player).showTrashBinAttack()
            }
            // 更新拥有的制作图数量
            this.equipmentBpBoxComp.updateEquipmentBpBoxInfo('DEL', this.equipmentInfo['en'], 1)
            this.equipmentInfo = null
        }

        // 隐藏绘制相关节点
        this.node.parent.active = false
        this.toolBoxNode.active = false
        this.colorBoardNode.active = false
        find('Canvas/UI/Joystick Panel').active = true
        find('Canvas/UI/Attack Button').active = true
        find('Canvas').getComponent(GameManager).isGamePaused = false

        // 制作图列选项颜色恢复
        this.equipmentBpBoxComp.resetAllItemColor()
        
        // 颜料全部清空
        this.blueprintCoverNode.removeAllChildren()
        for (let i=0; i<this.node.children.length; i++) {
            this.node.children[i].getComponent(Sprite).color = new Color().fromHEX('ffffff')
        }

        // 装备说明文本设置为空
        this.equipmentInfoNode.getComponent(Label).string = '未选择制作图'

        // 清空撤销重做堆栈
        this.clearUndoStack()
        this.clearRedoStack()
    }

    /** 
     * @zh
     * 玩家点击了存放按钮。
    */
    onStoreButtonClicked() {
        // 检查是否已经绘制完毕
        if (!this.checkIsDrawingFinished()) {
            return
        }

        // 查看背包是否已满
        if (this.checkIsBackpackFull()) {
            return
        }

        // 播放点击音效
        this.audioSource.playOneShot(this.clickAudio)

        // 自定义武器使用AI鉴定
        if (this.equipmentInfo['en'].indexOf('customWeapon') > -1) {
            if (this.isAIChecking) {
                this.utils.showFlashMessage('AI正在鉴定中...', 1)
                return
            }

            this.AICheck()
            this.isEquipOrStore = 'store'
            this.isAIChecking = true
            return
        }

        this.utils.showFlashMessage(`已存放到背包中`, 1)
        find('Canvas/Backpack Board').getComponent(Backpack).updateBackpackInfo('ADD', this.equipmentInfo['en'], 1, this.equipmentInfo)

        // 颜料全部清空
        this.blueprintCoverNode.removeAllChildren()
        for (let i=0; i<this.node.children.length; i++) {
            this.node.children[i].getComponent(Sprite).color = new Color().fromHEX('ffffff')
        }

        // 装备说明文本设置为空
        this.equipmentInfoNode.getComponent(Label).string = '未选择制作图'
        this.equipmentInfo = null

        // 清空撤销重做堆栈
        this.clearUndoStack()
        this.clearRedoStack()
    }

    /** 
     * @zh
     * 是否可以装备，即颜料是否填入到了正确位置。
    */
    checkIsDrawingFinished() {
        if (!this.equipmentInfo) {
            this.utils.showFlashMessage('需要从左侧选择一张制作图', 1)
            this.audioSource.playOneShot(this.wrongAudio)
            return false
        }

        // 针对自定义武器
        if (!this.blueprintCoverNode.children.length) {
            this.utils.showFlashMessage('还没有填入任何颜料哦', 1)
            this.audioSource.playOneShot(this.wrongAudio)
            return false
        }

        // 颜色子节点数量少于JSON中的颜色点，说明颜色没放全，不行
        let points = this.equipmentInfo['points']
        if (this.blueprintCoverNode.children.length < points.length) {
            this.utils.showFlashMessage('需要将颜料填入到正确位置', 1)
            this.audioSource.playOneShot(this.wrongAudio)
            return false
        }

        // 如果JSON中每个点的颜色都和blueprintCoverNode对应子节点的颜色一致，那么符合
        // 根据【索引-颜色十六进制】来判断
        let colorArray = []
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let itemIndexFromName = parseInt(this.blueprintCoverNode.children[i].name.split('-')[1])
            let colorHex = this.blueprintCoverNode.children[i].getComponent(Sprite).color.toHEX()
            colorArray.push(`${itemIndexFromName}-${colorHex}`)
        }

        for (let i=0; i<points.length; i++) {
            let x = points[i][0]
            let y = points[i][1]
            let itemIndex = x + y * 8
            let colorHex = points[i][2]
            
            // 有一个点对不上就直接排除
            if (colorArray.indexOf(`${itemIndex}-${colorHex}`) == -1) {
                return false
            }
        }

        return true
    }

    /** 
     * @zh
     * 检查背包是否已满。
    */
    checkIsBackpackFull() {
        let backpackInfo = JSON.parse(localStorage.getItem('lp-backpack'))
        if (backpackInfo && Object.keys(backpackInfo).length >= 28) {
            this.utils.showFlashMessage(`背包已经满啦`, 1)
            this.audioSource.playOneShot(this.wrongAudio)
            return true
        }
        return false
    }

    /** 
     * @zh
     * 恢复Blueprint Cover节点先前状态。
    */
    restoreBlueprintCover(data: Array<any>) {
        // 把当前的颜料返还到color board中，更新缓存中的颜料数量
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let colorItem = this.blueprintCoverNode.children[i]
                
            // 颜料数量回收+1
            let colorHex = colorItem.getComponent(Sprite).color.toHEX()
            this.colorBoardComp.updateColorData(colorHex, 1)
            colorItem.destroy()
        }

        // 根据操作数据恢复blueprint cover节点先前状态
        for (let i=0; i<data.length; i++) {
            let itemData = data[i]
            let itemName = itemData[0]
            let itemPos = itemData[1]
            let itemColorHex = itemData[2]

            // 添加像素块
            let colorItem = this.spawnColorItem(itemName, itemPos, itemColorHex)
            this.blueprintCoverNode.addChild(colorItem)

            // 颜料数量-1
            this.colorBoardComp.updateColorData(itemColorHex, -1)
        }
    }

    /** 
     * @zh
     * 恢复像素块指示器先前状态。
    */
    restoreIndicator(data: Array<any>) {
        let isActive = data[0]
        if (!isActive) {
            this.itemIndicatorNode.active = false
            return
        }

        let pos = data[1]
        let width = data[2]
        let height = data[3]

        this.itemIndicatorNode.active = true
        this.itemIndicatorNode.setPosition(pos)
        this.itemIndicatorNode.getComponent(UITransform).width = width
        this.itemIndicatorNode.getComponent(UITransform).height = height
    }

    /** 
     * @zh
     * 恢复操作，用于撤销重做功能。
    */
    restoreOperation(operationInfo: object) {
        let selectedEquipment = operationInfo['equipment']
        let selectedTool = operationInfo['tool']
        let selectedColor = operationInfo['color']
        let bpCoverData = operationInfo['bpCoverData']
        let indicatorData = operationInfo['indicatorData']
        let scaleXY = operationInfo['scaleXY']

        this.equipmentBpBoxComp.selectnEquipmentBpBoxItemByName(selectedEquipment)
        this.toolBoxComp.selectToolByName(selectedTool)
        this.colorBoardComp.selectColorItemByName(selectedColor)
        this.restoreBlueprintCover(bpCoverData)
        this.restoreIndicator(indicatorData)
        this.node.parent.setScale(scaleXY[0], scaleXY[1])
    }

    /** 
     * @zh
     * 撤销。
    */
    undo() {
        if (this.undoStack.length == 0) {
            return
        }

        // undo前先把当前的JSON数据添加到redo堆栈中
        this.addToRedoStack()

        // 按照数据复原
        let operationInfo = this.undoStack.pop()
        this.restoreOperation(operationInfo)

        this.audioSource.playOneShot(this.clickAudio)
    }

    /** 
     * @zh
     * 将网格JSON数据添加到undo堆栈中，清空redo堆栈。
    */
    addToUndoStack() {
        // 堆栈已满的话则先把最开始的数据删除
        if (this.undoStack.length >= this.maxStackLength) {
            this.undoStack.shift()
        }

        let bpCoverData = []
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let item = this.blueprintCoverNode.children[i]
            bpCoverData.push([item.name, new Vec3(item.position), item.getComponent(Sprite).color.toHEX()])
        }

        let indicatorNodeData = [
            this.itemIndicatorNode.active,
            new Vec3(this.itemIndicatorNode.position),
            this.itemIndicatorNode.getComponent(UITransform).width,
            this.itemIndicatorNode.getComponent(UITransform).height,
        ]

        let operationInfo = {
            'equipment': this.equipmentBpBoxComp.selectedEquipmentBpBoxItem ? this.equipmentBpBoxComp.selectedEquipmentBpBoxItem.name : '',
            'tool': this.toolBoxComp.selectedTool,
            'color': this.colorBoardComp.selectedColor,
            'bpCoverData': bpCoverData,
            'indicatorData': indicatorNodeData,
            'scaleXY': [this.node.parent.scale.x, this.node.parent.scale.y]
        }

        // 新添加的数据如果和上一个一样，则不添加
        if (this.undoStack.length == 0 || JSON.stringify(operationInfo) != JSON.stringify(this.undoStack[-1])) {
            this.undoStack.push(operationInfo)
        }
    }
    
    /** 
     * @zh
     * 重做。
    */
    redo() {
        if (this.redoStack.length == 0) {
            return
        }

        // redo前先把当前的JSON数据添加到undo堆栈中
        this.addToUndoStack()

        // 按照数据复原
        let operationInfo = this.redoStack.pop()
        this.restoreOperation(operationInfo)

        this.audioSource.playOneShot(this.clickAudio)
    }
    
    /** 
     * @zh
     * 将网格JSON数据添加到redo堆栈中。
    */
    addToRedoStack() {
        if (this.redoStack.length >= this.maxStackLength) {
            this.redoStack.shift()
        }
        
        let bpCoverData = []
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let item = this.blueprintCoverNode.children[i]
            bpCoverData.push([item.name, new Vec3(item.position), item.getComponent(Sprite).color.toHEX()])
        }

        let indicatorNodeData = [
            this.itemIndicatorNode.active,
            new Vec3(this.itemIndicatorNode.position),
            this.itemIndicatorNode.getComponent(UITransform).width,
            this.itemIndicatorNode.getComponent(UITransform).height,
        ]

        let operationInfo = {
            'equipment': this.equipmentBpBoxComp.selectedEquipmentBpBoxItem ? this.equipmentBpBoxComp.selectedEquipmentBpBoxItem.name : '',
            'tool': this.toolBoxComp.selectedTool,
            'color': this.colorBoardComp.selectedColor,
            'bpCoverData': bpCoverData,
            'indicatorData': indicatorNodeData,
            'scaleXY': [this.node.parent.scale.x, this.node.parent.scale.y]
        }

        // 新添加的数据如果和上一个一样，则不添加
        if (this.redoStack.length == 0 || JSON.stringify(operationInfo) != JSON.stringify(this.redoStack[-1])) {
            this.redoStack.push(operationInfo) 
        }
    }

    /** 
     * @zh
     * 清空undo堆栈，玩家点击使用或者存放后清空。
    */
    clearUndoStack() {
        this.undoStack = []
    }

    /** 
     * @zh
     * 清空redo堆栈，一般就是有新操作时执行一下。
    */
    clearRedoStack() {
        this.redoStack = []
    }

    /** 
     * @zh
     * 根据蓝图内容进行绘制。
    */
    draw(equipmentInfo:object) {
        // 所有方块颜色设置为白色
        for (let i=0; i<this.node.children.length; i++) {
            this.node.children[i].getComponent(Sprite).color = new Color().fromHEX('ffffff')
        }

        // 设置制作图上的物品信息
        this.setEquipmentInfo(equipmentInfo)

        if (!equipmentInfo) {
            return
        }

        // 给指定坐标上的方块设置指定颜色
        let points = equipmentInfo['points']
        for (let i=0; i<points.length; i++) {
            let x = points[i][0]
            let y = points[i][1]
            let itemIndex = x + y * 8
            let colorHex = points[i][2]
            this.node.children[itemIndex].getComponent(Sprite).color = new Color().fromHEX(colorHex)
        }
    }

    /** 
     * @zh
     * AI鉴定。
    */
    AICheck() {
        // 获取blueprintCover节点上的item位置和颜色
        let colorArray = []
        for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
            let itemIndexFromName = parseInt(this.blueprintCoverNode.children[i].name.split('-')[1])
            let color = this.blueprintCoverNode.children[i].getComponent(Sprite).color
            let r = color.r
            let g = color.g
            let b = color.b
            colorArray.push(`${itemIndexFromName}-${r},${g},${b}`)
        }
        
        this.sendToServer(colorArray)
    }

    /** 
     * @zh
     * 将玩家绘制的8x8像素数据发送到服务器。
    */
    sendToServer(colorArray: any[]) {
        const xhr = new XMLHttpRequest();
        const url = 'http://192.168.0.105:5000/ai'

        let self = this
        xhr.onreadystatechange = function(){
            if (this.readyState == 4 && this.status == 200){
                // 关闭鉴定动画
                self.node.parent.getChildByName('magnifier').destroy()

                const data=JSON.parse(xhr.responseText)
                if (data['status'] != 'success') {
                    self.aiResultBoardNode.active = true
                    self.aiResultBoardNode.children[0].children[0].getComponent(Label).string = '哎呀，AI打瞌睡了~\n给了武器30分~'
                    self.setCustomWeaponDamage(30)
                }
                else {
                    let score = data['msg']['score']        // 分数会被设置到自定义武器上
                    let content = data['msg']['content']    // AI评测内容

                    // 显示ai鉴定结果
                    self.aiResultBoardNode.active = true

                    // 打字机效果
                    let scheduleCount = 0
                    let typingText = ()=>{
                        if (scheduleCount < content.length) {
                            self.aiResultBoardNode.children[0].children[0].getComponent(Label).string += content[scheduleCount]
                            scheduleCount++
                        }
                        else {
                            self.unschedule(typingText)
                        }
                    }
                    
                    self.unschedule(typingText)
                    self.aiResultBoardNode.children[0].children[0].getComponent(Label).string = ''
                    self.schedule(typingText, 0.01)

                    // 将AI评分设置为自定义武器伤害值
                    self.setCustomWeaponDamage(score)
                }         
            }
        }

        // 请求错误时执行的逻辑
        xhr.onerror = function() {
            self.node.parent.getChildByName('magnifier').destroy()
            self.aiResultBoardNode.active = true
            self.aiResultBoardNode.children[0].children[0].getComponent(Label).string = '哎呀，AI打瞌睡了~\n给了武器30分~'
            self.setCustomWeaponDamage(30)
        }

        // 发送请求
        xhr.open('POST', url)
        xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
        xhr.send(JSON.stringify(colorArray))

        // 显示鉴定动画
        this.node.parent.addChild(instantiate(this.magnifierPrefab))
    }

    /** 
     * @zh
     * 设置自定义武器攻击力。
    */
    setCustomWeaponDamage(score: number) {
        // 原先武器伤害值确定标准：颜料数量+颜色种类数量
        // let hexArray = []
        // for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
        //     let item = this.blueprintCoverNode.children[i]
        //     let colorHex = item.getComponent(Sprite).color.toHEX()
        //     if (hexArray.indexOf(colorHex) == -1) {
        //         hexArray.push(colorHex)
        //     }
        // }
        //   let damage = Math.round(Math.random() * 20 * hexArray.length + 5 * hexArray.length+this.blueprintCoverNode.children.length)

        // 现在武器伤害值确定标准：AI评分
        this.equipmentInfo['damage'] = score
    }

    /** 
     * @zh
     * 隐藏AI鉴定结果面板。
    */
    hideAIResultBoardAndEquipOrStore() {
        // 隐藏评测面板
        this.aiResultBoardNode.active = false

        // AI鉴定的伤害值
        let damage = this.equipmentInfo['damage']

        // 更新拥有的制作图数量
        this.equipmentBpBoxComp.updateEquipmentBpBoxInfo('DEL', this.equipmentInfo['en'], 1)

        // 制作图列选项颜色恢复
        this.equipmentBpBoxComp.resetAllItemColor()

        // 如果是使用装备
        if (this.isEquipOrStore == 'equip') {
            // 将对应颜色添加到Custom Weapon节点上
            this.utils.showFlashMessage(`已装备自定义武器，攻击力为${damage}!`, 2)

            // 将武器节点图片删除
            let weaponNode = find('Canvas/Player/Weapon')
            weaponNode.getComponent(Sprite).spriteFrame = null

            // 先将原来武器节点的颜色恢复
            let customWeaponNode = find('Canvas/Player/Weapon/Custom Weapon')
            for (let i =0; i<customWeaponNode.children.length; i++) {
                customWeaponNode.children[i].getComponent(Sprite).color = new Color(0, 0, 0, 0)
            }
        
            let points = []     // 记录对应索引上的颜色
            for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
                let item = this.blueprintCoverNode.children[i]
                let itemIndex = parseInt(item.name.split('-')[1])
                let colorHex = item.getComponent(Sprite).color.toHEX()
                customWeaponNode.children[itemIndex].getComponent(Sprite).color = new Color().fromHEX(colorHex)
                points.push([itemIndex, colorHex])
            }
            
            localStorage.setItem('lp-weapon', this.equipmentInfo['en']+'-'+damage.toString()+'-'+JSON.stringify(points))

            find('Canvas/Player').getComponent(Player).weaponInfo = this.equipmentInfo
        }

        // 如果是存放到背包中
        else {
            this.equipmentInfo['en'] = 'customWeapon' + Date.now().toString()

            for (let i=0; i<this.blueprintCoverNode.children.length; i++) {
                let item = this.blueprintCoverNode.children[i]
                let itemIndex = parseInt(item.name.split('-')[1])
                let colorHex = item.getComponent(Sprite).color.toHEX()
                this.equipmentInfo['points'].push([itemIndex, colorHex])
            }

            this.utils.showFlashMessage(`已存放到背包中`, 1)
            find('Canvas/Backpack Board').getComponent(Backpack).updateBackpackInfo('ADD', this.equipmentInfo['en'], 1, this.equipmentInfo)
        }

        // AI鉴定结束
        this.isAIChecking = false

        // 隐藏绘制相关节点
        this.node.parent.active = false
        this.toolBoxNode.active = false
        this.colorBoardNode.active = false
        find('Canvas/UI/Joystick Panel').active = true
        find('Canvas/UI/Attack Button').active = true
        find('Canvas').getComponent(GameManager).isGamePaused = false

        // 颜料全部清空
        this.blueprintCoverNode.removeAllChildren()
        for (let i=0; i<this.node.children.length; i++) {
            this.node.children[i].getComponent(Sprite).color = new Color().fromHEX('ffffff')
        }

        // 装备说明文本设置为空
        this.equipmentInfoNode.getComponent(Label).string = '未选择制作图'
        this.equipmentInfo = null

        // 清空撤销重做堆栈
        this.clearUndoStack()
        this.clearRedoStack()
    }

    /** 
     * @zh
     * 画布放大。
    */
    scaleUp() {
        this.addToUndoStack()
        this.clearRedoStack()
        this.audioSource.playOneShot(this.clickAudio)
        this.node.parent.setScale(this.node.parent.scale.x+0.1, this.node.parent.scale.y+0.1, 1)
    }

    /** 
     * @zh
     * 画布缩小。
    */
    scaleDown() {
        this.addToUndoStack()
        this.clearRedoStack()
        this.audioSource.playOneShot(this.clickAudio)
        this.node.parent.setScale(this.node.parent.scale.x-0.1, this.node.parent.scale.y-0.1, 1)
    }
}


